﻿using System;
using System.Collections.Generic;
using System.Runtime.Remoting;
using EasyHook;
using APIMonLib;
using System.Diagnostics;
using System.Threading;

using CPN;
using System.IO.Pipes;
using System.IO;

namespace APIMon {
	class APIMonMain {

		static char requestKey() {
			return Console.ReadKey(true).KeyChar;
		}

		private static Func<int, string, string> expand = (num, what) => num <= 0 ? what : expand(num - 1, what) + what;

		private CPNetBuilder cpn_builder = new CPNetBuilder();
		private MessageServer message_server = new MessageServer();

		private static string generateFileName(Process p) {
			string result = string.Empty;
			DateTime dt = DateTime.Now;
			result += dt.Minute + "." + dt.Second + "." + (int)dt.Millisecond / 100;
			result = p.MainModule.ModuleName + "_" + result + ".csv";
			return result;
		}

		const string REPORT_DIRECTORY_BASE = "Z:\\Reports\\";

		private static string createReportDirectory() {
			DateTime dt = DateTime.Now;
			string report_dir = REPORT_DIRECTORY_BASE + dt.Year + "." + dt.Month + "." + dt.Day + "."+dt.Hour+"." + +dt.Minute + "." + dt.Second + "\\";
			if (!Directory.Exists(report_dir)) {
				Directory.CreateDirectory(report_dir);
			}
			return report_dir;
		}

		private void MainNoProcessStart() {
			message_server.watchProcesses(new string[] { 
														"msimn", 
														"smallftpd", 
														"Test_AcceptCon", 
														"Test_CreateProcess", 
														"BindShell_Pipe", 
														"iexplore", 
														"opera", 
														"far", 
														"Test_Files_Handles", 
														"cmd" 
														},
											false,
											null
										);
			ThreadJob();
		}

		private void ThreadJob() {
			bool do_exit = false;
			while (!do_exit) {
				Thread.Sleep(100);
				switch (requestKey()) {
					case 'q': { do_exit = true; break; }
					case 'p': {
							Console.WriteLine("(g)eneral or (s)pecial " + expand(2, "=>"));
							switch (requestKey()) {
								case 'g': { Place.printStatistics(Place.PrintLevel.Medium); break; }
								case 's': {
										Console.WriteLine("Enter the place id to print:");
										string str_id = Console.ReadLine();
										try {
											int place_id = int.Parse(str_id);
											foreach (Place place in Place.places_created) {
												if (place.id == place_id) {
													Console.WriteLine("\n Report for place: " + place.name_);
													Console.WriteLine(place.toString());
												}
											}
										} catch {
										}
										break;
									}
							}
							break;
						}
				}
			}
		}

		private void startUIThread() {
			ThreadStart job = new ThreadStart(ThreadJob);
			Thread thread = new Thread(job);
			thread.Start();
		}

		public APIMonMain() {
			cpn_builder.assembleCPN();
			message_server.initEnvironment();
		}

		private void runExperiment(ProgramStartList descr_list) {
			string report_directory = createReportDirectory();
			foreach (ProgramStartDescription program_start_decr in descr_list) {
				try {
					Process process = message_server.startProcessAndInject(program_start_decr);

					string report_file_path = report_directory + generateFileName(process);
					FileStream fs = new FileStream(report_file_path, FileMode.CreateNew);
					StreamWriter sw = new StreamWriter(fs);
					try {
						Console.WriteLine("Waiting for process to end...");
						bool result = message_server.waitForProcessToEnd(process, program_start_decr.max_running_time);
						Console.WriteLine("Writting report to file: " + report_file_path);
						#region Write  a report to file
						message_server.waitForTheEndOfProcessing();
						sw.WriteLine(program_start_decr);
						Place.writeStatistics(Place.PrintLevel.Medium, sw);
						sw.WriteLine(result ? "Exited" : "Killed");
					} finally {
						sw.Close();
					}
					#endregion

					//cleaning up
					Place.clearAllPlaces();
					System.GC.Collect();
				} catch (Exception ExtInfo) {
					Console.WriteLine("There was an error while running target: "+program_start_decr.image_path+"\r\n{0}", ExtInfo.ToString());
					//throw ExtInfo;
				}
			}
		}

		static void Main(string[] args) {
			APIMonMain api_mon = new APIMonMain();
			if (args.Length >= 1) {
				if (args[0].Equals("/noprocess1")) {
					api_mon.MainNoProcessStart();
					return;
				}
			}

			const int std_time = 20;

			#region Program start description
			ProgramStartList descr_list = new ProgramStartList();
			descr_list.last().image_dir = "C:\\Windows\\system32";
			descr_list.last().image_filename = "cmd.exe";
			descr_list.last().command_line = " /c dir";
			descr_list.last().max_running_time = std_time;
			descr_list += " /c dir c:\\Windows\\system32";
			descr_list += " /c dir c:\\Windows";
			descr_list += new ProgramStartDescription();
			descr_list.last().image_dir = "C:\\Program Files\\Outlook Express";
			descr_list.last().image_filename = "msimn.exe";
			descr_list.last().command_line = "";
			descr_list.last().max_running_time = std_time;
			descr_list += new ProgramStartDescription();
			descr_list.last().image_dir = "C:\\Program Files\\Internet Explorer";
			descr_list.last().image_filename = "iexplore.exe";
			descr_list.last().command_line = " http://bbc.com";
			descr_list.last().max_running_time = std_time;
			descr_list += new ProgramStartDescription();
			descr_list.last().image_dir = "C:\\Program Files\\Opera";
			descr_list.last().image_filename = "opera.exe";
			descr_list.last().command_line = " http://bbc.com";
			descr_list.last().max_running_time = std_time;
			descr_list += new ProgramStartDescription();
			descr_list.last().image_dir = "C:\\Program Files\\Far";
			descr_list.last().image_filename = "far.exe";
			descr_list.last().command_line = " ";
			descr_list.last().max_running_time = std_time;
			descr_list += new ProgramStartDescription();
			descr_list.last().image_dir = "C:\\Program Files\\Messenger";
			descr_list.last().image_filename = "msmsgs.exe";
			descr_list.last().command_line = " ";
			descr_list.last().max_running_time = std_time;
			descr_list += new ProgramStartDescription();
			descr_list.last().image_dir = "C:\\Program Files\\Movie Maker";
			descr_list.last().image_filename = "moviemk.exe";
			descr_list.last().command_line = " ";
			descr_list.last().max_running_time = std_time;
			descr_list += new ProgramStartDescription();
			descr_list.last().image_dir = "C:\\Program Files\\MSN\\MSNCoreFiles";
			descr_list.last().image_filename = "msn6.exe";
			descr_list.last().command_line = " ";
			descr_list.last().max_running_time = std_time;
			descr_list += new ProgramStartDescription();
			descr_list.last().image_dir = "C:\\Program Files\\Windows Media Player";
			descr_list.last().image_filename = "wmplayer.exe";
			descr_list.last().command_line = " ";
			descr_list.last().max_running_time = std_time;
			descr_list += new ProgramStartDescription();
			descr_list.last().image_dir = "C:\\Program Files\\WinRar";
			descr_list.last().image_filename = "WinRar.exe";
			descr_list.last().command_line = " ";
			descr_list.last().max_running_time = std_time;
			descr_list += new ProgramStartDescription();
			descr_list.last().image_dir = "C:\\Windows";
			descr_list.last().image_filename = "explorer.exe";
			descr_list.last().command_line = " ";
			descr_list.last().max_running_time = std_time;
			descr_list += descr_list.last().getExecutables();
			descr_list += new ProgramStartDescription();
			descr_list.last().image_dir = "C:\\Windows\\system32";
			descr_list.last().image_filename = "accwiz.exe";
			descr_list.last().command_line = " ";
			descr_list.last().max_running_time = std_time;
			descr_list += descr_list.last().getExecutables();
			//descr_list += new ProgramStartDescription();
			//descr_list.last().image_dir = "C:\\Documents and Settings\\amd\\Desktop";
			//descr_list.last().image_filename = "Test_Files_Handles.exe";
			////descr_list.last().command_line = " 50 10000";
			//descr_list.last().command_line = " ";
			//descr_list.last().max_running_time = 60 * 4; 
			#endregion

			api_mon.startUIThread();

			api_mon.runExperiment(descr_list);

			api_mon.message_server.closeEnvironment();
			Console.WriteLine("\nPress enter to finish.");
			Console.ReadLine();
			Environment.Exit(0);
		}


	}
}


#region Commented out

//AnonymousPipeServerStream forStdIn = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable);
//AnonymousPipeServerStream forStdOut = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable);
//AnonymousPipeServerStream forStdErr = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable);

//Process process = message_server.startProcessAndInject("C:\\Documents and Settings\\amd\\Desktop\\Test_Files_Handles.exe", " 10 10000 50",
//    forStdIn.ClientSafePipeHandle.DangerousGetHandle(), forStdOut.ClientSafePipeHandle.DangerousGetHandle(), forStdErr.ClientSafePipeHandle.DangerousGetHandle());

//Process process = message_server.startProcessAndInject("C:\\Windows\\system32\\mspaint.exe", " ",
//    forStdIn.ClientSafePipeHandle.DangerousGetHandle(), forStdOut.ClientSafePipeHandle.DangerousGetHandle(), forStdErr.ClientSafePipeHandle.DangerousGetHandle());

//Process process = message_server.startProcessAndInject("C:\\Windows\\system32\\netstat.exe", " -ano",
//    forStdIn.ClientSafePipeHandle.DangerousGetHandle(), forStdOut.ClientSafePipeHandle.DangerousGetHandle(), forStdErr.ClientSafePipeHandle.DangerousGetHandle());


//Important:
//We need to close local handles. In that way when child process closes pipe we get EOF. If we don't close local child handles we never get EOF and the last read blocks forever
//forStdIn.DisposeLocalCopyOfClientHandle();
//forStdOut.DisposeLocalCopyOfClientHandle();
//forStdErr.DisposeLocalCopyOfClientHandle();

//StreamWriter swr = new StreamWriter(forStdIn);
//StreamReader srd = new StreamReader(forStdOut);
//StreamReader srderr = new StreamReader(forStdErr);



//Other type of code


//Process process = new Process();
//ProcessStartInfo psi = new ProcessStartInfo();
//psi.FileName = "C:\\Windows\\system32\\cmd.exe";
//psi.Arguments = " ";
////psi.RedirectStandardError = true;
////psi.RedirectStandardInput = true;
////psi.RedirectStandardOutput = true;
//psi.UseShellExecute = false;
//psi.CreateNoWindow = true;
//process.StartInfo = psi;
//process.EnableRaisingEvents = false;
//process.Start();

//message_server.injectLibrary(process); 
#endregion